﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Windows.Forms;
using PSTaskDialog;

namespace ArduinoSketch.Core
{
    public class AvrdudeUploader : Uploader
    {
        private HardwareProfile _hardwareProfile;
        private string _portName;
        private System.IO.Ports.SerialPort _serialPort;

        /// <summary>
        /// 
        /// </summary>
        public AvrdudeUploader()
        {
            this._serialPort = new System.IO.Ports.SerialPort(); 
        }

        private void MessageErrorDialog(string Title, string MainInstruction, string Content)
        {
            PSTaskDialog.cTaskDialog.MessageBox(
                            Title,
                            MainInstruction,
                            Content,
                            eTaskDialogButtons.OK,
                            eSysIcons.Error);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="portName"></param>
        public void SetCurrentPortName(string portName)
        {
            _portName = portName;
            this._serialPort.PortName = _portName;
        }

        /// <summary>
        /// 
        /// </summary>
        private void DtrEnable()
        {
            try
            {
                if ((_hardwareProfile.UploadPotocal == "stk500v1") ||
                (_hardwareProfile.UploadPotocal == "stk500"))
                {
                    _serialPort.Open();
                    _serialPort.DtrEnable = true;
                    System.Threading.Thread.Sleep(100);
                    _serialPort.DtrEnable = false;
                    _serialPort.Close();
                }
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="hardwareProfile"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public string uploadViaBootloader(HardwareProfile hardwareProfile, string fileName)
        {
            List<string> commandDownloader = new List<string>();

            try
            {
                _hardwareProfile = hardwareProfile; 

                string protocol = _hardwareProfile.UploadPotocal;
                if (protocol.Equals("stk500"))
                    protocol = "stk500v1";

                commandDownloader.Add("-c" + protocol);
                commandDownloader.Add("-P" + this._portName);
                commandDownloader.Add("-b" + _hardwareProfile.UploadSpeed);
                if (protocol.Equals("stk500"))
                    commandDownloader.Add("-D"); // don't erase
                commandDownloader.Add("-Uflash:w:" + fileName + ":i");

                Console.Out.WriteLine(fileName);
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            

            return avrdude(commandDownloader);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="programmer"></param>
        public void burnBootloader(string programmer)
        {
            try
            {
                this.burnBootloader(this.getProgrammerCommands(programmer));
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
           
        }        

        /// <summary>
        /// 
        /// </summary>
        /// <param name="programmer"></param>
        /// <returns></returns>
        private IEnumerable<string> getProgrammerCommands(string programmer)
        {
            List<string> param = new List<string>();
            Programer _programer = Utils.GetProgramer(programmer);

            param.Add("-c" + _programer.Protocol);
    
            if ("usb".Equals(_programer.Communication)) {
                param.Add("-Pusb");
            }
            else if ("serial".Equals(_programer.Communication))
            {
                param.Add("-P" + this._portName); 
            }
             
            if (_programer.Force)
                param.Add("-F");

            return param;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        private string burnBootloader(IEnumerable<string> param)
        {
            string _strlLoader = "";
            List<string> bootloader = new List<string>();
            string _avrdudeResult = "";
            try
            {
                List<string> fuses = new List<string>();
                _strlLoader = string.Format(@"{0}\hardware\bootloaders\{1}", Utils.LoadArduinoTargetConfig().ArduinoDirectory, _hardwareProfile.BootLoaderPath);
                fuses.Add("-e"); // erase the chip

                fuses.Add("-Ulock:w:" + _hardwareProfile.BootloaderUnlockBit + ":m");
                fuses.Add("-Uefuse:w:" + _hardwareProfile.BootloaderExtenedFuses + ":m");
                fuses.Add("-Uhfuse:w:" + _hardwareProfile.BootloaderHighFuses + ":m");
                fuses.Add("-Ulfuse:w:" + _hardwareProfile.BootloaderLowFuses + ":m");
                avrdude(param, fuses);

                System.Threading.Thread.Sleep(1000); 
                
                bootloader.Add("-Uflash:w:" + _strlLoader + ":i");
                bootloader.Add("-Ulock:w:" + _hardwareProfile.BootloaderLockBit + ":m");

                _avrdudeResult = avrdude(param, bootloader);
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
           

            return _avrdudeResult;

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="param1"></param>
        /// <param name="param2"></param>
        /// <returns></returns>
        private string avrdude(IEnumerable<string> param1, IEnumerable<string> param2)
        {
            List<string> p = new List<string>(param1);
            string _avrdudeResult = "";
            try
            {
                p.AddRange(param2);
                _avrdudeResult = avrdude(p);
            }
            catch (Exception)
            {
                
                throw;
            }


            return _avrdudeResult;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        private string avrdude(IEnumerable<string> param)
        {
            string _appPath = Application.StartupPath;
            string _avrdudeResult = "";

            try
            {
                List<string> commandDownloader = new List<string>();
                string avrdude = String.Format(@"{0}\avrdude.exe", Application.StartupPath);
                commandDownloader.Add("-q");
                commandDownloader.Add("-pm" + _hardwareProfile.Mcu.Substring(6));

                commandDownloader.AddRange(param); 

                _avrdudeResult = executeUploadCommand(avrdude, commandDownloader);   
            }
            catch (Exception)
            {
                
                throw;
            }

            return _avrdudeResult;     
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        protected override string executeUploadCommand(string filename, IEnumerable<string> param)
        {
            string _args = "";
            string _avrdudeResult = "";
            try
            {
                foreach (var p in param)
                {
                    _args += p + " ";
                }
                this.DtrEnable();

                Console.Out.WriteLine(_args);

                _avrdudeResult = this.RunProcess(filename, _args);
            }
            catch (Exception)
            {
                
                throw;
            }

            return _avrdudeResult;
        } 
    }
}
